#include "HeadersAndMacros.h"

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: OpenConnection
--
--	DATE: Feb. 20, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: Renz Barron
--
--	PROGRAMMER: Renz Barron
--
--	INTERFACE: SOCKET Connections::OpenConnection(int port, int broadcastType, char* ip, 
--                                                bool type, bool protocol)
--
--      int port: Port to connect to.
--
--      int broadcastType: Type of streaming; MULTICAST, DOWNLOAD, UPLOAD, STREAMING, 
--                         etc.
--
--      char* ip: IP address of the server we're connecting to.
--
--      BOOL type: Whether or not this is a server or client.
--
--      BOOL protocol: TCP or UDP?
--
--	RETURNS: SOCKET - A fully connected socket of either the TCP or UDP protocol.
--
--	NOTES: Creates a socket and establishes a connection with an available server.
--         Many wrapper funtions involved in the lower level code. Functions used
--         depends on whether or not the connection is made server side or client side.
--
/*---------------------------------------------------------------------------------------*/
SOCKET Connections::OpenConnection(int port, int broadcastType, char* ip, bool type, bool protocol)
{
	SOCKET connection;

	if((connection = SocketFunctions::Create_Socket(protocol)) == INVALID_SOCKET)
	{
		return INVALID_SOCKET;
	}

	if(!SocketFunctions::SetSocketOptions(broadcastType, type, connection))
	{
		return INVALID_SOCKET;
	}
	
	if(type == SERVER)
	{
		if(!StartServer(port, broadcastType, type, connection, protocol))
		{	
			return INVALID_SOCKET;
		}
	}
	
	else
	{
		if(protocol == UDP)
		{
			return connection;
		}
    
		if(!Start_TCPClient(ip, port, connection))
		{
			return INVALID_SOCKET;
		}
	}
  
	return connection;
}

/*--------------------------------------------------------------------------------------
--
--	FUNCTION: ListenForConnection
--
--	DATE: Feb. 21, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: Renz Barron
--
--	PROGRAMMER: Renz Barron
--
--	INTERFACE: bool Connections::ListenForConnection(SOCKET socket)
--
--      SOCKET socket: The socket we need to listen on. 
--
--	RETURNS: void
--
--	NOTES: Wrapper function to for the 'listen' function.
--
/*------------------------------------------------------------------------------------*/
bool Connections::ListenForConnection(SOCKET socket)
{
	char str[SIZEERROR]; 
	
	/* Listen for connections; 5 maximum */
	if(listen(socket, 5) == SOCKET_ERROR)
	{
		sprintf_s(str, SIZEERROR, "Could not listen for new connections: %d", WSAGetLastError());
		
		WSAFatal(str, "Error: 'ListenForConnection' function");
	}

	return true;
}

/*---------------------------------------------------------------------------------------------
--
--	FUNCTION: EstablishConnection
--
--	DATE: Feb. 20, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: Renz Barron
--
--	PROGRAMMER: Renz Barron
--
--	INTERFACE: bool Connections::EstablishConnection(struct sockaddr_in server, 
--                                                   SOCKET connection)
--
--      struct sockaddr_in server: Structure of the server to connect to.
--
--      SOCKET connection: Descriptor of the an unconnected socket.
--
--	RETURNS: void
--
--	NOTES: Formally create the connection to the server on this socket. Used for connecting
--         a client.
--
/*-------------------------------------------------------------------------------------------*/
bool Connections::EstablishConnection(struct sockaddr_in server, SOCKET connection)
{
	if(connect(connection, (struct sockaddr *)&server, sizeof(server)) == -1)
	{
		char str[SIZEERROR]; 
    
		sprintf_s(str, SIZEERROR, "Cannot connect to server: %d", WSAGetLastError());
		
		WSAFatal(str, "ERROR: 'EstablishConnection'");
	
		return false;
	}
	
	return true;
}

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: StartServer
--
--	DATE: Feb. 20, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: Renz Barron
--
--	PROGRAMMER: Renz Barron
--
--	INTERFACE: bool Connections::StartServer(int port, int broadcastType, bool type, 
--                                           SOCKET connection, BOOL protocol)
--
--      int port: The server's port on which to connect to.
--
--      int broadcastType: What type of sending to be done; MULTICAST, DOWNLOAD, etc.
--
--      bool type: Client or server; in this case it will always be of type client.
--
--      SOCKET connection: Descriptor of the an unconnected socket.
--
--      BOOL protocol: TCP or UDP.
--
--	RETURNS: bool
--
--	NOTES: Formally create a server side connection with this protocol.
--
/*---------------------------------------------------------------------------------------*/
bool Connections::StartServer(int port, int broadcastType, bool type, SOCKET connection, BOOL protocol)
{
	if(!SocketFunctions::SocketBind(connection, port, broadcastType, type))
    {
		return false;
	}

	if(protocol == TCP)
	{  
		if(!ListenForConnection(connection))
		{
			return false;
		}
	}
  
	return true;
}

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: GetServer
--
--	DATE: Feb. 20, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: Renz Barron
--
--	PROGRAMMER: Renz Barron
--
--	INTERFACE: void GetServer(char* ip, int port, struct sockaddr_in *server)
--
--      char* ip; IP address of the requested server.
--      int port: Port of the server to connect to.
--      struct sockaddr_in *server: Which server to connect to.
--
--	RETURNS: void
--
--	NOTES: Fills in the 'server' structure to be used by the server to tell it where
--         it will send data to.
--
/*---------------------------------------------------------------------------------------*/
void Connections::GetServer(char* ip, int port, struct sockaddr_in *server)
{
	struct hostent *hp;

	memset((char *)server, 0, sizeof(struct sockaddr_in));
	
	server->sin_family = AF_INET;
	server->sin_port = htons(port);
  
	if((hp = gethostbyname(ip)) == NULL)
	{
		char str[SIZEERROR]; 
    
		sprintf_s(str, SIZEERROR, "Unknown server address: %d", WSAGetLastError());
		
		WSAFatal(str, "ERROR: gethostbyname");
	}
  
	memcpy((char *)&server->sin_addr, hp->h_addr, hp->h_length);
}

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: Start_TCPClient
--
--	DATE: Feb. 20, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: Renz Barron
--
--	PROGRAMMER: Renz Barron
--
--	INTERFACE: bool Connections::Start_TCPClient(char *ip, int port, SOCKET connection)
--
--      char* ip: Establish a client connection (using wrapper functions upon wrapper
--                functions =\...) with a server of a TCP type connection. 
--  
--      int port: The server's port on which to connect to.
--
--      SOCKET connection: Descriptor of the an unconnected socket.
--
--	RETURNS: bool
--
--	NOTES: Formally create the connection to the server on this socket.
--
/*---------------------------------------------------------------------------------------*/
bool Connections::Start_TCPClient(char *ip, int port, SOCKET connection)
{
	struct sockaddr_in server;

	GetServer(ip, port, &server);
	
	if(!EstablishConnection(server, connection))
    {
		return false;
	}

	return true;
}

/*-------------------------------------------------------------------------
--
--	FUNCTION: WSAFatal
--
--	DATE: Feb. 16, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: Aman Abdulla
--
--	PROGRAMMER: Renz Barron
--
--	INTERFACE: void Connections::WSAFatal(char* message, char* header)
--
--      char* message: Description of error.
--      char* header: Title displayed in the message box.
--
--	RETURNS: void
--
--	NOTES: Exits program based on the WSAGetLastError value.
--
/*-----------------------------------------------------------------------*/
void Connections::WSAFatal(char* message, char* header)
{
	WSACleanup();
	
	MessageBox(NULL, (LPCWSTR)message, (LPCWSTR)header, MB_OK);
	
	exit(WSAGetLastError());
}

